package Viewer;

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Locale;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;

import Export.FetchNCBI;

/**
 * The main framework of GeneScapes. By loading data a {@link Viewer} gets
 * embedded.
 * 
 * @author Stefan Lorenz <br>
 *         <b>Date:</b> 17.11.2011
 */
@SuppressWarnings("serial")
public class Framework extends JFrame {

	/**
	 * JDesktopPane to add other windows
	 */
	private JDesktopPane desktop;
	/**
	 * The memory bar show the current use of memory
	 */
	private JProgressBar memoryBar;
	/**
	 * The optimized filechooser
	 */
	private FileChooserX fc;

	/**
	 * Constructor of the framework of GeneScapes contains {@link Viewer} or
	 * other embedded panels.
	 */
	public Framework() {
		JComponent.setDefaultLocale(Locale.ENGLISH);
		// gnu gpl v3
		System.out.println("GeneScapes v1.02  Copyright (C) 2012  Stefan Lorenz\n" + "This program comes with ABSOLUTELY NO WARRANTY;\n" + "This is free software, and you are welcome to redistribute it under certain conditions;\n"
				+ "show main menu -> 'Help' -> 'About GeneScapes' for details.");
		System.out.println("************************************");
		System.out.println(System.getProperty("os.name"));
		System.out.println(System.getProperty("os.arch"));
		System.out.println(System.getProperty("os.version"));
		System.out.println("************************************");
		System.out.println("Starting GeneScapes with " + (int) (Runtime.getRuntime().maxMemory() / 1000000) + " MB of memory...");
		// create window title, icons etc.
		this.setTitle("GeneScapes");
		this.setIconImage(new ImageIcon(Framework.class.getResource("../resources/cbvIcon.png")).getImage());

		this.setLayout(new BorderLayout());
		this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		// create all listener panels etc.
		addFrameWorkListener();
		createDesktopPane();

		// info panel containing memory logo etc.
		JPanel infoPanel = new JPanel();
		infoPanel.setBackground(Color.BLACK);
		infoPanel.setLayout(new BorderLayout());
		// panel for displaying memory
		JPanel memoryPanel = new JPanel();
		memoryPanel.setBackground(Color.BLACK);
		JLabel memLab = new JLabel("Used Memory:");
		memLab.setForeground(Color.WHITE);
		memoryPanel.add(memLab);
		this.memoryBar = new JProgressBar(0, (int) (Runtime.getRuntime().maxMemory() / 1000000));
		this.memoryBar.setStringPainted(true);
		memoryPanel.add(memoryBar);
		infoPanel.add(memoryPanel, BorderLayout.WEST);
		// IGB icon
		ImageIcon icon = new ImageIcon(Framework.class.getResource("../resources/fhg25.png"));
		JLabel logo = new JLabel(icon);
		infoPanel.add(logo, BorderLayout.EAST);
		logo.addMouseListener(new MouseListener() {
			@Override
			public void mouseReleased(MouseEvent e) {
				if (java.awt.Desktop.isDesktopSupported()) {
					java.awt.Desktop desktop = java.awt.Desktop.getDesktop();

					if (desktop.isSupported(java.awt.Desktop.Action.BROWSE)) {
						java.net.URI uri;
						try {
							uri = new java.net.URI("http://www.igb.fraunhofer.de/en/competences/molecular-biotechnology/genomics-transcriptomics-proteomics.html");
							desktop.browse(uri);
						} catch (URISyntaxException e1) {
							e1.printStackTrace();
						} catch (IOException e1) {
							e1.printStackTrace();
						}

					}
				}
			}

			@Override
			public void mouseClicked(MouseEvent e) {
			}

			@Override
			public void mousePressed(MouseEvent e) {
			}

			@Override
			public void mouseEntered(MouseEvent e) {
				setCursor(new Cursor(Cursor.HAND_CURSOR));
			}

			@Override
			public void mouseExited(MouseEvent e) {
				setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
			}
		});

		// create menu bar filcechooser etc.
		this.fc = new FileChooserX();
		this.fc.setFileHidingEnabled(false);
		this.fc.setPreferredSize(new Dimension(600, 500));
		this.setJMenuBar(new FrameworkMenu(this));
		this.setVisible(true);
		this.add(infoPanel, BorderLayout.SOUTH);
	}

	/**
	 * Creates and adds the {@link JDesktopPane} with background, pictures etc.
	 * of GeneScapes
	 */
	private void createDesktopPane() {
		this.desktop = new JDesktopPane() {

			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				Graphics2D g2 = (Graphics2D) g;
				g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
				g2.setColor(Color.DARK_GRAY);
				g2.fillRect(0, 0, this.getWidth(), this.getHeight());
				ImageIcon icon = new ImageIcon(Framework.class.getResource("../resources/muster.png"));
				Composite normal = g2.getComposite();
				AlphaComposite ac1 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f);
				g2.setComposite(ac1);

				g2.drawImage(icon.getImage(), (int) (this.getWidth() * 0.1), (this.getHeight() / 2) - icon.getIconHeight() / 2, this);

				g2.setComposite(normal);
				g.setFont(new Font("frutiger", Font.BOLD, 34));
				g.setColor(new Color(28, 163, 138));
				g.drawString("GeneScapes", (int) (this.getWidth() * 0.1) + 110, (int) (this.getHeight() / 2) + 5);
				g.setColor(Color.LIGHT_GRAY);
				g.setFont(new Font("arial", Font.BOLD, 18));
				g.drawString("An annotate-by-click browser", (int) (this.getWidth() * 0.1 + 110), (int) (this.getHeight() / 2 + 25));
			}

		};
		this.add(desktop, BorderLayout.CENTER);
	}

	/**
	 * Adds a {@link WindowListener} to the GeneScapes {@link Framework}.
	 */
	private void addFrameWorkListener() {
		this.addWindowListener(new WindowListener() {
			public void windowOpened(WindowEvent e) {
			}

			public void windowIconified(WindowEvent e) {
			}

			public void windowDeiconified(WindowEvent e) {
			}

			public void windowDeactivated(WindowEvent e) {
			}

			public void windowClosing(WindowEvent e) {
				Object[] options = { "OK", "Cancel" };
				int n = JOptionPane.showOptionDialog(Framework.this, "Do you really want to quit?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
				if (n == 0) {
					System.out.println("...bye");
					System.exit(0);
				}
			}

			public void windowClosed(WindowEvent e) {
			}

			public void windowActivated(WindowEvent e) {
			}
		});
	}

	/**
	 * Adds a {@link Viewer} to the {@link Framework}.
	 * 
	 * @param viewer
	 *            to browse
	 */
	public void addViewerToDesktop(final Viewer viewer) {
		final JInternalFrame iframe = new JInternalFrame(viewer.getName(), true, true, true, true);
		iframe.setDefaultCloseOperation(JInternalFrame.EXIT_ON_CLOSE);
		iframe.setFrameIcon(new ImageIcon(Framework.class.getResource("../resources/cbvIcon20.png")));
		iframe.addInternalFrameListener(new InternalFrameListener() {

			public void internalFrameOpened(InternalFrameEvent e) {
			}

			public void internalFrameIconified(InternalFrameEvent e) {
			}

			public void internalFrameDeiconified(InternalFrameEvent e) {
			}

			public void internalFrameDeactivated(InternalFrameEvent e) {
			}

			public void internalFrameClosing(InternalFrameEvent e) {
				int selection = JOptionPane.showConfirmDialog(iframe, "Are you sure you want close the browser window?", "Confirm", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
				if (selection == JOptionPane.OK_OPTION) {
					viewer.closeAll();
					closeInternalFrame(iframe);
				}
			}

			public void internalFrameClosed(InternalFrameEvent e) {
			}

			public void internalFrameActivated(InternalFrameEvent e) {
			}
		});
		iframe.setLayout(new BorderLayout());
		iframe.add(new Menue(viewer), BorderLayout.NORTH);
		iframe.add(viewer, BorderLayout.CENTER);
		iframe.setLocation(0, 0);
		iframe.setBounds(0, 0, this.getWidth() - 5, this.getHeight() - 90);
		iframe.setVisible(true);
		viewer.setMemoryBar(this.memoryBar);
		this.desktop.add(iframe);

		try {
			iframe.setSelected(true);
			iframe.setMaximizable(true);
			iframe.setMaximum(true);
		} catch (PropertyVetoException e) {
			System.err.println("Can not maximize the browser window");
			e.printStackTrace();
		}
	}

	/**
	 * Adds a arbitrary panel to a {@link JInternalFrame} and opens it in the
	 * {@link Framework}.
	 * 
	 * @param panel
	 *            to show in the framework window
	 * @param title
	 *            name of the window
	 */
	public void addPanelToDesktop(JPanel panel, String title) {
		JInternalFrame iframe = new JInternalFrame(title, true, true, true, true);
		iframe.add(panel);
		iframe.setVisible(true);
		iframe.setSize(new Dimension(600, 400));
		this.desktop.add(iframe);
	}

	/**
	 * Manages open local files.
	 */
	public void openLocalFile() {
		if (this.fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
			setCursor(new Cursor(Cursor.WAIT_CURSOR));
			Viewer viewer = null;
			File file = this.fc.getSelectedFile();
			// reading file except GeneScapes session file
			if (!file.getName().endsWith(".gss")) {
				try {
					viewer = new Viewer(file);
					this.addViewerToDesktop(viewer);
					viewer.getFileChooser().setCurrentDirectory(file);
				} catch (Exception e) {
					System.err.println("Can not parse file " + file.getAbsolutePath());
					e.printStackTrace();
					return;
				}
			}
			// reading GeneScapes session file
			else if (file.getName().endsWith(".gss")) {
				String line = "";
				try {
					BufferedReader r = new BufferedReader(new FileReader(file));
					String startline = r.readLine();

					while ((line = r.readLine()) != null) {
						int trackIndex = Integer.parseInt(line.split("\t")[0]);
						File openFile = new File(line.split("\t")[1]);
						// use track index 1 to open Viewer
						if (trackIndex == 1) {
							viewer = new Viewer(openFile);
							if (viewer == null)
								break;

						} else {
							ReferenceChooser referenceChooser = new ReferenceChooser(openFile);
							viewer.addTracks(viewer.checkPath(line.substring(String.valueOf(trackIndex).length())), trackIndex, referenceChooser.getReference(), referenceChooser.getRange());
						}
					}
					r.close();
					// set position and range of Viewer
					if (viewer != null) {
						try {
							viewer.setPositionOfBrowser(1, Integer.valueOf(startline.split("\t")[0]));
							viewer.setRangeOfBrowser(Integer.valueOf(startline.split("\t")[1]));
						} catch (NullPointerException e) {
							viewer.setPositionOfBrowser(1, 0);
							viewer.setRangeOfBrowser(1000);
							e.printStackTrace();
							System.err.println("Can not read position or range of browser from GeneScapes session file...use standard values");
						}
						this.addViewerToDesktop(viewer);
						viewer.getFileChooser().setCurrentDirectory(this.fc.getSelectedFile());
						viewer.resetTrackPositions();
					}
				} catch (IOException e) {
					System.err.println("Can not read GeneScapes session file");
					e.printStackTrace();
				}
			}
		}
		setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
	}

	public void openLastFile() {
		if (new File("lastOpened.gss").exists()) {
			setCursor(new Cursor(Cursor.WAIT_CURSOR));
			Viewer viewer = null;
			File file = new File("lastOpened.gss");

			// reading GeneScapes session file
			String line = "";
			try {
				BufferedReader r = new BufferedReader(new FileReader(file));
				String startline = r.readLine();

				while ((line = r.readLine()) != null) {
					int trackIndex = Integer.parseInt(line.split("\t")[0]);
					File openFile = new File(line.split("\t")[1]);
					// use track index 1 to open Viewer
					if (trackIndex == 1) {
						viewer = new Viewer(openFile);
						if (viewer == null)
							break;

					} else {
						ReferenceChooser referenceChooser = new ReferenceChooser(openFile);
						viewer.addTracks(viewer.checkPath(line.substring(String.valueOf(trackIndex).length())), trackIndex, referenceChooser.getReference(), referenceChooser.getRange());
					}
				}
				r.close();
				// set position and range of Viewer
				if (viewer != null) {
					try {
						viewer.setPositionOfBrowser(1, Integer.valueOf(startline.split("\t")[0]));
						viewer.setRangeOfBrowser(Integer.valueOf(startline.split("\t")[1]));
					} catch (NullPointerException e) {
						viewer.setPositionOfBrowser(1, 0);
						viewer.setRangeOfBrowser(1000);
						e.printStackTrace();
						System.err.println("Can not read position or range of browser from GeneScapes session file...use standard values");
					}
					this.addViewerToDesktop(viewer);
					viewer.getFileChooser().setCurrentDirectory(this.fc.getSelectedFile());
					viewer.resetTrackPositions();
				}
			} catch (IOException e) {
				System.err.println("Can not read GeneScapes session file");
				e.printStackTrace();
			}

		}
		setCursor(new Cursor(Cursor.DEFAULT_CURSOR));

	}

	/**
	 * Manages downloads via accession ID from NCBI database.<br>
	 * Requires Python and BioPython for non windows systems.
	 */
	public void openNcbiGenBankID() {
		String id = JOptionPane.showInputDialog(this, "Enter Refseq or GenBank ID");
		if (id == null)
			return;
		if (id.equals(""))
			return;
		setCursor(new Cursor(Cursor.WAIT_CURSOR));
		// if file not exists yet start downloading
		if (!new File("genomes/" + id + ".gbk").exists()) {
			System.out.println("Downloading: " + id + ".gbk");
			new FetchNCBI(id, new File("genomes/" + id + ".gbk"));
		}
		// if file already exists
		if (new File("genomes/" + id + ".gbk").exists()) {
			Viewer viewer = new Viewer(new File("genomes/" + id + ".gbk"));
			this.addViewerToDesktop(viewer);
		} else
			System.err.println("Can not find file....downloading error?! Check you genome directory!");

		setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
	}

	/**
	 * Returns the FAQ panel.
	 * 
	 * @return panel with FAQs
	 */
	public JPanel createFAQ() {
		JPanel panel = new JPanel();
		panel.setLayout(new GridLayout(2, 1));
		JLabel info = new JLabel("<html>FAQ:<br><br><br>" + "Coming soon...</html>");
		panel.add(info);
		return panel;
	}

	/**
	 * Returns the getting started panel.
	 * 
	 * @return panel with getting started introduction
	 */
	public JPanel createGettinStarted() {
		JPanel panel = new JPanel();
		panel.setLayout(new GridLayout(5, 1));
		JLabel label = new JLabel("A Flash Tutorial");
		panel.add(label);
		JButton openFile = new JButton("Load Embl or Genbank file");
		JButton controls = new JButton("Controlling");
		JButton layout = new JButton("Change layout");
		JButton loadTrack = new JButton("Load track");
		panel.add(openFile);
		panel.add(controls);
		panel.add(loadTrack);
		panel.add(layout);
		return panel;
	}

	/**
	 * Returns the about panel.
	 * 
	 * @return panel with about text
	 */
	public JPanel createAbout() {

		JPanel panel2 = new JPanel() {

			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				Graphics2D g2 = (Graphics2D) g;
				g2.setColor(Color.BLACK);
				g2.fillRect(0, 0, this.getWidth(), this.getHeight());
				ImageIcon icon = new ImageIcon(Framework.class.getResource("../resources/muster.png"));
				Composite normal = g2.getComposite();
				AlphaComposite ac1 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f);
				g2.setComposite(ac1);

				g2.drawImage(icon.getImage(), 5, (this.getHeight() / 2) - icon.getIconHeight() / 2, this);

				g2.setComposite(normal);
				g2.setFont(new Font("arial", Font.BOLD, 25));
				g2.setColor(Color.WHITE);
				g2.drawString("ABOUT", (int) (this.getWidth() * 0.1), this.getHeight() / 4);
				g2.setFont(new Font("arial", Font.BOLD, 15));
				g2.drawString("GeneScapes v1.01", (int) (this.getWidth() * 0.1 + 4), this.getHeight() / 4 + 20);
				g2.drawString("Contact: stlo@igb.fhg.de or kso@igb.fhg.de", (int) (this.getWidth() * 0.1 + 4), this.getHeight() / 4 + 40);

			}
		};
		String gplv3 = "GeneScapes is a interactive genome browser to visualize and improve gene models\n"
				+ " Copyright (C) 2012  Stefan Lorenz\n"
				+ "This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License.\n"
				+ "This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n"
				+ "You should have received a copy of the GNU General Public License along with this program; if not, see http://www.gnu.org/licenses";

		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());

		JTextArea area = new JTextArea();
		area.setLineWrap(true);
		area.setText(gplv3);

		JScrollPane scrollpane = new JScrollPane(area);

		panel.add(panel2, BorderLayout.CENTER);
		panel.add(scrollpane, BorderLayout.SOUTH);
		return panel;
	}

	/**
	 * Closes internal frame of framework.
	 * 
	 * @param iframe
	 *            window to close
	 */
	private void closeInternalFrame(JInternalFrame iframe) {
		System.out.println("************************************");
		System.out.println("Closed: Internal Browser Frame " + iframe.getTitle());
		System.out.println("************************************");
		iframe.dispose();
		iframe = null;
		this.memoryBar.setValue((int) ((float) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1000000));
	}

	/**
	 * Sets a new look and feel and updates all components of GeneScapes
	 * 
	 * @param lookAndFeel
	 *            name of the look and feel
	 */
	public void updateLookAndFeel(String lookAndFeel) {
		try {
			if (lookAndFeel == "system")
				UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
			else if (lookAndFeel == "nimbus")
				UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
			else if (lookAndFeel == "metal")
				UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
		} catch (Exception e) {
		}

		SwingUtilities.updateComponentTreeUI(this);
		SwingUtilities.updateComponentTreeUI(this.fc);
		SwingUtilities.updateComponentTreeUI(this.desktop);
		SwingUtilities.updateComponentTreeUI(this.memoryBar);
	}
}
